package com.jx.driving.protocol.terminal.codec;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.jx.driving.protocol.terminal.annotation.BcdAnnotation;
import com.jx.driving.protocol.terminal.annotation.ByteAnnotation;
import com.jx.driving.protocol.terminal.annotation.ByteArrayAnnotation;
import com.jx.driving.protocol.terminal.annotation.DwordAnnotation;
import com.jx.driving.protocol.terminal.annotation.DynamicByteArrayAnnotation;
import com.jx.driving.protocol.terminal.annotation.LimitedListAnnotation;
import com.jx.driving.protocol.terminal.annotation.LimitedStringAnnotation;
import com.jx.driving.protocol.terminal.annotation.ListAnnotation;
import com.jx.driving.protocol.terminal.annotation.ObjectAnnotation;
import com.jx.driving.protocol.terminal.annotation.StringAnnotation;
import com.jx.driving.protocol.terminal.annotation.TailByteArrayAnnotation;
import com.jx.driving.protocol.terminal.annotation.TerminalAnnotation;
import com.jx.driving.protocol.terminal.annotation.WordAnnotation;

/**
 * 类读写器
 * 
 * @author shenliuyang
 *
 *         2016年8月1日下午2:35:59
 */
public class ClassReaderWriter {

	public static final List<Class<? extends Annotation>> fieldReaderWriterAnnotationClass = Arrays.asList(
			BcdAnnotation.class, ByteAnnotation.class, ByteArrayAnnotation.class, DwordAnnotation.class,
			DynamicByteArrayAnnotation.class, LimitedStringAnnotation.class, StringAnnotation.class,
			WordAnnotation.class, LimitedListAnnotation.class, ObjectAnnotation.class, ListAnnotation.class,
			TailByteArrayAnnotation.class);
	private List<FieldReaderWriter> readerWriterList = new ArrayList<>();

	private Class<?> clazz;

	public ClassReaderWriter(Class<?> clazz) throws Exception {
		this.clazz = clazz;
		parseFieldReaderWriter();
	}

	/**
	 * 从流中读取数据并解析成对象返回
	 */
	public <T> T read(InputStream in) throws Exception {
		Object source = clazz.newInstance();
		for (FieldReaderWriter frw : readerWriterList) {
			frw.read(in, source);
		}
		return (T) source;
	}

	/**
	 * 把对象中的数据写入到流中
	 */
	public void write(OutputStream out, Object source) throws Exception {
		for (FieldReaderWriter frw : readerWriterList) {
			frw.write(out, source);
		}
	}

	private void parseFieldReaderWriter() throws Exception {
		if (!clazz.isAnnotationPresent(TerminalAnnotation.class)) {
			throw new IllegalArgumentException("class " + clazz + " must annotated with " + TerminalAnnotation.class);
		}
		this.readerWriterList = new ArrayList<>();
		parseFieldReaderWriter(readerWriterList, clazz);
		Collections.sort(this.readerWriterList);
	}

	private void parseFieldReaderWriter(List<FieldReaderWriter> list, Class<?> c) throws Exception {
		if (c == null) {
			return;
		}
		parseFieldReaderWriter(list, c.getSuperclass());
		for (Field f : c.getDeclaredFields()) {
			for (Class<? extends Annotation> annotationClass : fieldReaderWriterAnnotationClass) {
				if (!f.isAnnotationPresent(annotationClass)) {
					continue;
				}
				parseFieldReaderWriter(list, f, f.getAnnotation(annotationClass));
			}
		}
	}

	private void parseFieldReaderWriter(List<FieldReaderWriter> list, Field field, Annotation annotation)
			throws Exception {
		Class<?> clazz = annotation.getClass();
		Method codecMethod = clazz.getMethod("codec");
		Method sizeMethod = clazz.getMethod("value");
		Method orderMethod = clazz.getMethod("order");
		Class<? extends FieldReaderWriter> frwClass = (Class<? extends FieldReaderWriter>) codecMethod
				.invoke(annotation, null);
		FieldReaderWriter frw = frwClass.getConstructor(Field.class, Integer.TYPE, Integer.TYPE).newInstance(field,
				sizeMethod.invoke(annotation), orderMethod.invoke(annotation));
		list.add(frw);
	}

}
